Read the Accept header via @Headers('accept') and branch the response format. Use @Res({ passthrough: true }) to set Content-Type and return a StreamableFile for non-JSON formats while returning plain data for JSON. For a reusable solution build a content-negotiation interceptor that reads Accept and transforms via map() in the RxJS pipeline.
Check accept?.includes('text/csv') instead of strict equality — clients may send 'text/csv, /'.
Always provide a JSON fallback — it is the default and most widely supported format.
Set Content-Type explicitly for non-JSON responses — NestJS defaults to application/json.
For a reusable solution build a ContentNegotiationInterceptor that reads Accept and calls map().
Consider caching negotiated responses separately — CSV and JSON of the same data are different resources.